package net.lab0.fractal.gui;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Point2D;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;

import net.lab0.fractal.abstracts.AbstractManageableUI2DFractal;
import net.lab0.fractal.classes.RenderingCoordinates;
import net.lab0.fractal.fractals.mandelbrot.MandelbrotQuadTreeZoneInfo;
import net.lab0.tools.quadtree.BasicQuadTreeElement;
import net.lab0.tools.quadtree.DataQuadTreeNode;
import nu.xom.ParsingException;
import nu.xom.ValidityException;

@SuppressWarnings("serial")
public class MandelbrotComponent extends FractalComponent
{
    private boolean                                                            mandelbrotQuadTreeDisplayed;
    private int                                                                mandelbrotQuadTreeDepth    = 1;
    private MandelbrotQuadTree                                                 mandelbrotQuadTree;
    private int                                                                minimumDisplayedsQuareSize = 8;
    private DataQuadTreeNode<BasicQuadTreeElement, MandelbrotQuadTreeZoneInfo> highlightedNode;
    
    public MandelbrotComponent(AbstractManageableUI2DFractal fractal)
    {
        super(fractal);
        rebuildMandelbrotQuadTree(mandelbrotQuadTreeDepth);
        
        addMouseListener(new MouseAdapter()
        {
            @Override
            public void mousePressed(MouseEvent e)
            {
                popupTest(e);
            }
            
            @Override
            public void mouseReleased(MouseEvent e)
            {
                popupTest(e);
            }
        });
    }
    
    private void popupTest(MouseEvent e)
    {
        if (e.isPopupTrigger())
        {
            JMenu statsMenu = new JMenu("Statistics");
            JMenu subdivMenu = new JMenu("Subdivide");
            
            mousePosition = e.getPoint();
            RenderingCoordinates rc = fractal.getRenderingCoordinates();
            Point2D.Double p = rc.toRenderingCoordinates(mousePosition);
            
            for (final DataQuadTreeNode<BasicQuadTreeElement, MandelbrotQuadTreeZoneInfo> node : mandelbrotQuadTree.getNodesInRectangle(p, p))
            {
                if (node != null)
                {
                    JMenuItem currentTileStats = new JMenuItem(node.getPath());
                    statsMenu.add(currentTileStats);
                    currentTileStats.addMouseListener(new MouseAdapter()
                    {
                        @Override
                        public void mouseEntered(MouseEvent e)
                        {
                            System.out.println(node.getPath());
                            highlightedNode = node;
                            repaint();
                        }
                    });
                    currentTileStats.addActionListener(new ActionListener()
                    {
                        @Override
                        public void actionPerformed(ActionEvent e)
                        {
                            JFrame f = new MandelbrotZoneInformationFrame(node);
                            f.pack();
                            f.setVisible(true);
                            f.setExtendedState(JFrame.MAXIMIZED_BOTH);
                            f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                            
                            highlightedNode = null;
                            repaint();
                        }
                    });
                    
                    JMenu currentTileSubdivMenu = new JMenu(node.getPath());
                    currentTileSubdivMenu.addItemListener(new ItemListener()
                    {
                        @Override
                        public void itemStateChanged(ItemEvent e)
                        {
                            highlightedNode = node;
                            repaint();
                        }
                    });
                    subdivMenu.add(currentTileSubdivMenu);
                    
                    for (int i = node.getDepth() + 1; i <= Math.min(node.getTreeRoot().getMaxNodeDepth(), node.getDepth() + 10); ++i)
                    {
                        JMenuItem subdivMenuItem = new JMenuItem("to " + i);
                        currentTileSubdivMenu.add(subdivMenuItem);
                        final Integer tmpInt = new Integer(i);
                        subdivMenuItem.addActionListener(new ActionListener()
                        {
                            @Override
                            public void actionPerformed(ActionEvent e)
                            {
                                try
                                {
                                    mandelbrotQuadTree.setComputingDepth(tmpInt, node);
                                    highlightedNode = null;
                                    repaint();
                                }
                                catch (InterruptedException e1)
                                {
                                    JOptionPane.showMessageDialog(null, e1.getMessage(), "Can't compute " + e.getClass().getSimpleName(),
                                            JOptionPane.ERROR_MESSAGE);
                                    e1.printStackTrace();
                                }
                            }
                        });
                    }
                }
            }
            
            JPopupMenu popupMenu = new JPopupMenu("Options");
            popupMenu.add(statsMenu);
            popupMenu.add(subdivMenu);
            
            popupMenu.show(this, e.getX(), e.getY());
        }
    }
    
    @Override
    public void paintComponent(Graphics g)
    {
        super.paintComponent(g);
        
        Graphics2D g2d = (Graphics2D) g;
        
        RenderingCoordinates view = fractal.getRenderingCoordinates();
        
        Point2D.Double p1 = view.getScaledTopLeftCorner();
        Point2D.Double p2 = view.getScaledBottomRightCorner();
        
        if (mandelbrotQuadTreeDisplayed)
        {
            if (mandelbrotQuadTree == null)
            {
                rebuildMandelbrotQuadTree(mandelbrotQuadTreeDepth);
            }
            
            int maxDisplayDepth = mandelbrotQuadTree.getMaxNodeDepth();
            
            for (DataQuadTreeNode<BasicQuadTreeElement, MandelbrotQuadTreeZoneInfo> node : mandelbrotQuadTree.getNodesInRectangle(p1, p2))
            {
                if (!node.isLeafNode() || node.getDepth() > maxDisplayDepth)
                {
                    continue;
                }
                MandelbrotQuadTreeZoneInfo info = node.getNodeElement();
                
                Point start = view.toScreenCoordinates(node.getMinX(), node.getMaxY());
                Point end = view.toScreenCoordinates(node.getMaxX(), node.getMinY());
                int width = end.x - start.x;
                int height = end.y - start.y;
                if (height < minimumDisplayedsQuareSize || width < minimumDisplayedsQuareSize)
                {
                    maxDisplayDepth = node.getDepth() - 1;
                    continue;
                }
            }
            
            for (DataQuadTreeNode<BasicQuadTreeElement, MandelbrotQuadTreeZoneInfo> node : mandelbrotQuadTree.getNodesInRectangle(p1, p2))
            {
                if (node.getDepth() > maxDisplayDepth)
                {
                    continue;
                }
                if (!node.isLeafNode() && node.getDepth() < maxDisplayDepth)
                {
                    continue;
                }
                MandelbrotQuadTreeZoneInfo info = node.getNodeElement();
                
                Point start = view.toScreenCoordinates(node.getMinX(), node.getMaxY());
                Point end = view.toScreenCoordinates(node.getMaxX(), node.getMinY());
                int width = end.x - start.x;
                int height = end.y - start.y;
                if (info != null)
                {
                    if (info.isInside())
                    {
                        g2d.setColor(new Color(0f, 0.5f, 0.5f, 0.25f));
                        g2d.fillRect(start.x, start.y, width, height);
                        g2d.setColor(new Color(0f, 0.5f, 0.5f, 0.75f));
                        g2d.drawRect(start.x, start.y, width, height);
                    }
                    else if (!info.isBorder())
                    {
                        g2d.setColor(new Color(0.5f, 0.5f, 0f, 0.25f));
                        g2d.fillRect(start.x, start.y, width, height);
                        g2d.setColor(new Color(0.5f, 0.5f, 0f, 0.75f));
                        g2d.drawRect(start.x, start.y, width, height);
                    }
                    else
                    {
                        g2d.setColor(new Color(0.5f, 0f, 0.5f, 0.25f));
                        g2d.fillRect(start.x, start.y, width, height);
                        g2d.setColor(new Color(0.5f, 0f, 0.5f, 0.75f));
                        g2d.drawRect(start.x, start.y, width, height);
                    }
                }
                else
                {
                    g2d.setColor(new Color(0.5f, 0f, 0.5f, 0.25f));
                    g2d.fillRect(start.x, start.y, width, height);
                    g2d.setColor(new Color(0.5f, 0f, 0.5f, 0.75f));
                    g2d.drawRect(start.x, start.y, width, height);
                }
            }
        }
        
        if (highlightedNode != null)
        {
            Point start = view.toScreenCoordinates(highlightedNode.getMinX(), highlightedNode.getMaxY());
            Point end = view.toScreenCoordinates(highlightedNode.getMaxX(), highlightedNode.getMinY());
            int width = end.x - start.x;
            int height = end.y - start.y;
            
            g2d.setColor(new Color(1.0f, 1.0f, 1.0f, 0.25f));
            g2d.fillRect(start.x, start.y, width, height);
            g2d.setColor(new Color(1.0f, 0f, 0f, 0.75f));
            g2d.drawRect(start.x, start.y, width, height);
        }
    }
    
    @Override
    protected void fractalComponentMouseMoved(MouseEvent e)
    {
        super.fractalComponentMouseMoved(e);
        
        RenderingCoordinates rc = fractal.getRenderingCoordinates();
        Point2D.Double p = rc.toRenderingCoordinates(mousePosition);
        DataQuadTreeNode<BasicQuadTreeElement, MandelbrotQuadTreeZoneInfo> node = mandelbrotQuadTree.getLeafNodeAt(p);
        if (node != null)
        {
            setToolTipText(node.getPath());
        }
        else
        {
            setToolTipText("Out of bounds");
        }
    }
    
    private void rebuildMandelbrotQuadTree(int mandelbrotQuadTreeDepth)
    {
        if (mandelbrotQuadTree == null)
        {
            mandelbrotQuadTree = new MandelbrotQuadTree(256, 1024);
        }
        try
        {
            mandelbrotQuadTree.setComputingDepth(mandelbrotQuadTreeDepth);
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
    }
    
    public void setMandelbrotQuadTreeDepth(int mandelbrotQuadTreeDepth)
    {
        if (mandelbrotQuadTreeDepth >= 0)
        {
            this.mandelbrotQuadTreeDepth = mandelbrotQuadTreeDepth;
            rebuildMandelbrotQuadTree(mandelbrotQuadTreeDepth);
        }
    }
    
    public boolean isMandelbrotQuadTreeDisplayed()
    {
        return mandelbrotQuadTreeDisplayed;
    }
    
    public void setMandelbrotQuadTreeDisplayed(boolean mandelbrotQuadTreeDisplayed)
    {
        this.mandelbrotQuadTreeDisplayed = mandelbrotQuadTreeDisplayed;
    }
    
    public void saveTreeTo(File f) throws IOException
    {
        FileOutputStream fos = new FileOutputStream(f);
        mandelbrotQuadTree.toXML(fos);
    }
    
    public void loadTreeFrom(File f) throws IOException, ValidityException, ParsingException
    {
        FileInputStream fos = new FileInputStream(f);
        mandelbrotQuadTree.fromXML(fos);
    }
    
    public MandelbrotQuadTree getMandelbrotQuadTree()
    {
        return mandelbrotQuadTree;
    }
    
}
